package com.atguigu.gulimall.order.config;

import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

/**
 * 2022/4/28 16:29 星期四
 * MyRabbit
 * Created by ASIA 乔帅阳 .
 * --
 * --
 */
@Configuration
public class MyRabbit {

    @Autowired
    private RabbitTemplate rabbitTemplate;


    /**
     *  定制rabbitTemplate
     *   1、服务（Broker）收到消息就回调
     *      1、在配置文件配置spring.rabbitmq.publisher-confirm-type=correlated
     *      2、设置确认回调
     *      3、如果报错循环依赖问题把该方法拿出来单独配置
     *   2、消息正确抵达队列进行回调
     *      1、#开启发送消息抵达队列的确认
     *         spring.rabbitmq.publisher-returns=true
     *         #只要抵达了队列，以异步模式优先回调我们这个returnconfirm
     *         spring.rabbitmq.template.mandatory=true
     *      2、设置确认回调returnedCallback
     *
     *   3、消费端确认（保证每一个消息被正确消费，此时此可以broker删除这个消息）。
     *       1、默认是自动确认的，只要消息接收到，客户端会自动确认，服务端就会移除这个消息
     *         问题：
     *            我们收到很多消息，自动回复给服务器ack,只有一个消息处理成功，宕机了。发生消息丢失。
     *         解决：消费者手动确认模式。只要我们没有明确告诉MQ，货物被签收，没有ACK，消息就一直是unacked这个
     *               状态。即使Consumer宕机。消息不会丢失，会重新变为Ready状态，下一次有新的Consumer连接进来
     *               就发给他。
     *       2、如何签收：
     *          如果签收成功了就channel(信道).basicAck(deliveryTag,false);//deliveryTag信道内自增的值，是否批量
     *          ACK手动应答。channel(信道).basicNack(deliveryTag,false,true)第三个参数不为ture时消息还是会丢失不会重新入队
     *
     */

    @PostConstruct  //MyRabbitConfig对象创建完成之后在调用该方法
    public void initRabbitTemplate(){
        //设置确认回调
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback(){
            /**
             * 只要消息抵达服务Broker就b等于true
             * @param correlationData 当前消息的唯一关联数据（这个是消息的唯一ID）
             * @param b 消息是否成功收到
             * @param s 失败的原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {

                /**
                 * 1、做好消息确认机制（pulisher,consumer【手动ack】）
                 * 2、每一个发送的消息都在数据库做好记录，定期将失败的消息再次发送一遍
                 */
                System.out.println("confirm....correlationData【"+correlationData+"】==>b【"+b+"】==>s【"+s+"】");
            }
        });

        //设置消息抵达队列的确认回调
        rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback(){

            /**
             * 只要消息没有投递给指定的队列，就触发这个失败的回调
             * @param returnedMessage  投递失败的消息详细信息
             */

            /**
             * ReturnedMessage
             * @param returnedMessage  类中的4个参数
             *     private final Message message;  投递失败的消息详细信息
             *     private final int replyCode;    回复的状态码
             *     private final String replyText; 回复的文本内容
             *     private final String exchange;  当时这个消息发给哪个交换机
             *     private final String routingKey;当时这个消息用哪个路邮件
             */
            @Override
            public void returnedMessage(ReturnedMessage returnedMessage) {
                System.out.println(" Fail Message["+returnedMessage+"]");
            }
        });
    }
}
